// Copyright (c) 2022 Huawei Device Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "content/browser/media/ohos/ohos_media_player_renderer.h"

#include <memory>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "content/browser/media/ohos/ohos_media_player_renderer_web_contents_observer.h"
#include "content/browser/media/session/media_session_impl.h"
#include "content/browser/media/ohos/ohos_media_resource_getter_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "media/base/timestamp_constants.h"
#include "media/mojo/mojom/renderer_extensions.mojom.h"

namespace content {

namespace {
const float kDefaultVolume = 1.0;
constexpr double kPlaybackRateLevel0 = 0;
constexpr double kPlaybackRateLevel1 = 1;
constexpr double kPlaybackRateLevel2 = 1.25;
constexpr double kPlaybackRateLevel3 = 1.75;
constexpr double kPlaybackRateLevel4 = 2;
enum InterruptHint {
  INTERRUPT_HINT_NONE = 0,
  INTERRUPT_HINT_RESUME,
  INTERRUPT_HINT_PAUSE,
  INTERRUPT_HINT_STOP,
  INTERRUPT_HINT_DUCK,
  INTERRUPT_HINT_UNDUCK
};
}  // namespace

OHOSMediaPlayerRenderer::OHOSMediaPlayerRenderer(
    int process_id,
    int routing_id,
    WebContents* web_contents,
    mojo::PendingReceiver<RendererExtension> renderer_extension_receiver,
    mojo::PendingRemote<ClientExtension> client_extension_remote)
    : client_extension_(std::move(client_extension_remote)),
      has_error_(false),
      render_process_id_(process_id),
      routing_id_(routing_id),
      volume_(kDefaultVolume),
      renderer_extension_receiver_(this,
                                   std::move(renderer_extension_receiver)) {
  WebContentsImpl* web_contents_impl =
      static_cast<WebContentsImpl*>(web_contents);
  web_contents_muted_ = web_contents_impl && web_contents_impl->IsAudioMuted();

  if (web_contents) {
    web_contents_ = web_contents->GetWeakPtr();
    OHOSMediaPlayerRendererWebContentsObserver::CreateForWebContents(
        web_contents);
    web_contents_observer_ =
        OHOSMediaPlayerRendererWebContentsObserver::FromWebContents(
            web_contents);
    if (web_contents_observer_) {
      web_contents_observer_->AddMediaPlayerRenderer(this);
    }
  }
}

OHOSMediaPlayerRenderer::~OHOSMediaPlayerRenderer() {
  if (web_contents_observer_) {
    web_contents_observer_->RemoveMediaPlayerRenderer(this);
  }
}

void OHOSMediaPlayerRenderer::Initialize(
    media::MediaResource* media_resource,
    media::RendererClient* client,
#if BUILDFLAG(ARKWEB_VIDEO_ASSISTANT)
    media::RequestSurfaceCB request_surface_cb,
    media::VideoDecoderChangedCB decoder_changed_cb,
#endif // ARKWEB_VIDEO_ASSISTANT
    media::PipelineStatusCallback init_cb) {
  renderer_client_ = client;
  if (media_resource->GetType() != media::MediaResource::Type::KUrl) {
    DLOG(ERROR) << "MediaResource is not of Type URL";
    std::move(init_cb).Run(media::PIPELINE_ERROR_INITIALIZATION_FAILED);
    return;
  }

  url_params_ = std::make_unique<media::MediaUrlParams>(media_resource->GetMediaUrlParams());
  if (!url_params_) {
    LOG(ERROR) << "GetMediaUrlParams failed";
    std::move(init_cb).Run(media::PIPELINE_ERROR_INITIALIZATION_FAILED);
    return;
  }

  init_cb_ = std::move(init_cb);
  TryOrCreateMediaPlayer();
}

// LCOV_EXCL_START
void OHOSMediaPlayerRenderer::CreateMediaPlayer() {
    const std::string user_agent = GetContentClient()->browser()->GetUserAgent();
    std::vector<std::string> grantMediaFileAccessDirs;
    GetGrantMediaFileAccessDirs(grantMediaFileAccessDirs);

    if (!url_params_) {
      LOG(ERROR) << "CreateMediaPlayer failed, no url_params_";
      std::move(init_cb_).Run(media::PIPELINE_ERROR_INITIALIZATION_FAILED);
      return;
    }

    media_player_.reset(new media::OHOSMediaPlayerBridge(
        url_params_->media_url, url_params_->site_for_cookies,
        url_params_->top_frame_origin, user_agent,
        url_params_->storage_access_api_status,
        false,  // hide_url_log
        this, url_params_->allow_credentials, url_params_->is_hls, url_params_->headers, grantMediaFileAccessDirs));
    if (!media_player_) {
      LOG(ERROR) << "CreateMediaPlayer failed, no media_player_";
      std::move(init_cb_).Run(media::PIPELINE_ERROR_INITIALIZATION_FAILED);
      return;
    }
    media_player_->SetNativeWindowSurface(native_window_id_);
    int32_t ret = media_player_->Initialize();
    if (ret != 0) {
        LOG(ERROR) << "media player Initialize failed";
        std::move(init_cb_).Run(media::PIPELINE_ERROR_INITIALIZATION_FAILED);
    } else {
        initialized_ = true;
        std::move(init_cb_).Run(media::PIPELINE_OK);
        LOG(INFO) << "media player Initialize ok";
    }
}
// LCOV_EXCL_STOP

void OHOSMediaPlayerRenderer::SetLatencyHint(
    std::optional<base::TimeDelta> latency_hint) {}

void OHOSMediaPlayerRenderer::Flush(base::OnceClosure flush_cb) {
  std::move(flush_cb).Run();
}

void OHOSMediaPlayerRenderer::StartPlayingFrom(base::TimeDelta time) {
  if (has_error_ || !media_player_) {
    return;
  }
  media_player_->SeekTo(time);
  renderer_client_->OnBufferingStateChange(
      media::BufferingState::BUFFERING_HAVE_ENOUGH,
      media::BufferingStateChangeReason::BUFFERING_CHANGE_REASON_UNKNOWN);
}

void OHOSMediaPlayerRenderer::SetPlaybackRate(double playback_rate) {
  if (has_error_ || !media_player_) {
    LOG(ERROR) << "SetPlaybackRate, has_error or no media_player_";
    return;
  }
  if (playback_rate <= kPlaybackRateLevel0) {
    media_player_->Pause();
  } else {
    OHOS::NWeb::PlaybackRateMode mode;
    if (playback_rate < kPlaybackRateLevel1) {
      mode = OHOS::NWeb::PlaybackRateMode::SPEED_FORWARD_0_75_X;
    } else if (playback_rate < kPlaybackRateLevel2) {
      mode = OHOS::NWeb::PlaybackRateMode::SPEED_FORWARD_1_00_X;
    } else if (playback_rate < kPlaybackRateLevel3) {
      mode = OHOS::NWeb::PlaybackRateMode::SPEED_FORWARD_1_25_X;
    } else if (playback_rate < kPlaybackRateLevel4) {
      mode = OHOS::NWeb::PlaybackRateMode::SPEED_FORWARD_1_75_X;
    } else {
      mode = OHOS::NWeb::PlaybackRateMode::SPEED_FORWARD_2_00_X;
    }
    media_player_->SetPlaybackSpeed(mode);
    media_player_->Start();
  }
}

void OHOSMediaPlayerRenderer::InitiateScopedSurfaceRequest(
    InitiateScopedSurfaceRequestCallback callback) {}

void OHOSMediaPlayerRenderer::FinishPaint(int32_t fd) {
}

void OHOSMediaPlayerRenderer::SetNativeWindowSurface(int native_window_id) {
  LOG(INFO) << "SetMediaPlayerSurface, native_window_id:" << native_window_id;
  native_window_id_ = native_window_id;
  TryOrCreateMediaPlayer();
}

// LCOV_EXCL_START
void OHOSMediaPlayerRenderer::TryOrCreateMediaPlayer() {
  LOG(INFO) << "TryOrCreateMediaPlayer enter";
  bool wait_surface_created = native_window_id_ == -1;
  if (wait_surface_created) {
    LOG(INFO) << "TryOrCreateMediaPlayer wait_surface_created";
    return;
  }
  if (url_params_ == nullptr) {
    LOG(INFO) << "TryOrCreateMediaPlayer url_params_== nullptr";
    return;
  }
  CreateMediaPlayer();
}
// LCOV_EXCL_STOP

void OHOSMediaPlayerRenderer::OnFrameAvailable(int fd,
                                               uint32_t size,
                                               int32_t coded_width,
                                               int32_t coded_height,
                                               int32_t visible_width,
                                               int32_t visible_height,
                                               int32_t format) {
}

// LCOV_EXCL_START
media::OHOSMediaResourceGetter* OHOSMediaPlayerRenderer::GetMediaResourceGetter()
{
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  if (!media_resource_getter_.get()) {
    RenderProcessHost* host = RenderProcessHost::FromID(render_process_id_);

    // The RenderFrameHost/RenderProcessHost may have been destroyed already,
    // as there might be a delay between the frame closing and
    // MojoRendererService receiving a connection closing error.
    if (!host)
      return nullptr;

    BrowserContext* context = host->GetBrowserContext();
    media_resource_getter_ = std::make_unique<OHOSMediaResourceGetterImpl>(
        context, render_process_id_, routing_id_);
  }
  return media_resource_getter_.get();
}
// LCOV_EXCL_STOP

void OHOSMediaPlayerRenderer::OnMediaDurationChanged(base::TimeDelta duration) {
  if (duration.is_zero()) {
    duration = media::kInfiniteDuration;
  }

  if (duration_ != duration) {
    duration_ = duration;
    client_extension_->OnDurationChange(duration);
  }
}

void OHOSMediaPlayerRenderer::OnPlaybackComplete() {
  renderer_client_->OnEnded();
}

void OHOSMediaPlayerRenderer::OnError(int error) {
  if (error ==
      media::OHOSMediaPlayerBridge::MediaErrorType::MEDIA_ERROR_INVALID_CODE) {
    return;
  }
  LOG(ERROR) << __func__ << " Error: " << error;
  has_error_ = true;
  if (initialized_) {
    renderer_client_->OnError(media::PIPELINE_ERROR_EXTERNAL_RENDERER_FAILED);
  }
}

void OHOSMediaPlayerRenderer::OnVideoSizeChanged(int width, int height) {
  LOG(INFO) << "OHOSMediaPlayerRenderer::OnVideoSizeChanged enter";
  gfx::Size new_size = gfx::Size(width, height);
  if (video_size_ != new_size) {
    video_size_ = new_size;
    // Send via |client_extension_| instead of |renderer_client_|, so
    // MediaPlayerRendererClient can update its texture size.
    // MPRClient will then continue propagating changes via its RendererClient.
    client_extension_->OnVideoSizeChange(video_size_);
  }
}

void OHOSMediaPlayerRenderer::OnUpdateAudioMutingState(bool muted) {
  web_contents_muted_ = muted;
  UpdateVolume();
}

void OHOSMediaPlayerRenderer::OnWebContentsDestroyed() {
  web_contents_observer_ = nullptr;
}

void OHOSMediaPlayerRenderer::OnPlayerInterruptEvent(int32_t value) {
  if (web_contents_.get() == nullptr) {
    LOG(ERROR) << "web contents is nullptr";
    return;
  }
  MediaSessionImpl* mediaSession = MediaSessionImpl::Get(web_contents_.get());
  if (mediaSession == nullptr) {
    LOG(ERROR) << "get mediaSession is nullptr";
    return;
  }
  LOG(INFO) << "On Player InterruptEvent value:" << value;
  if (value == static_cast<int32_t>(INTERRUPT_HINT_PAUSE) ||
      value == static_cast<int32_t>(INTERRUPT_HINT_STOP)) {
    if (mediaSession->audioResumeInterval_ != 0) {
      intervalSinceLastSuspend_ = std::time(nullptr);
    }
    mediaSession->Suspend(content::MediaSession::SuspendType::kSystem);
  } else if (value == INTERRUPT_HINT_RESUME) {
    if (isNeedResume(mediaSession->audioResumeInterval_) &&
        mediaSession->IsSuspended()) {
      mediaSession->Resume(content::MediaSession::SuspendType::kSystem);
    }
  }
}

bool OHOSMediaPlayerRenderer::isNeedResume(int32_t resumeInterval) {
  return resumeInterval < 0 ||
      (resumeInterval > 0 &&
      std::time(nullptr) - intervalSinceLastSuspend_ <=
      static_cast<double>(resumeInterval));
}

void OHOSMediaPlayerRenderer::SetVolume(float volume) {
  volume_ = volume;
  UpdateVolume();
}

void OHOSMediaPlayerRenderer::UpdateVolume() {
  if (media_player_) {
    media_player_->SetVolume(volume_, web_contents_muted_);
  }
}

void OHOSMediaPlayerRenderer::OnAudioStateChanged(bool isAudible) {
  if (web_contents_.get() == nullptr) {
    LOG(ERROR) << "web contents is nullptr";
    return;
  }
  WebContentsImpl* web_contents_impl =
      static_cast<WebContentsImpl*>(web_contents_.get());
  if (isAudible) {
    web_contents_impl->AsWebContentsImplExt()->AddMediaPlayerAudibleCount();
    web_contents_.get()->OnAudioStateChanged();
  } else {
    web_contents_impl->AsWebContentsImplExt()->DelMediaPlayerAudibleCount();
    web_contents_.get()->OnAudioStateChanged();
  }
}

void OHOSMediaPlayerRenderer::OnPlayerSeekBack(base::TimeDelta back_time) {
  if (web_contents_.get() == nullptr) {
    LOG(ERROR) << "web contents is nullptr";
    return;
  }
  MediaSessionImpl* mediaSession = MediaSessionImpl::Get(web_contents_.get());
  if (mediaSession == nullptr) {
    LOG(ERROR) << "get mediaSession is nullptr";
    return;
  }
  LOG(INFO) << "SEEK_CLOSEST failure and seek back time: " << back_time;
  mediaSession->SeekTo(back_time);
}

base::TimeDelta OHOSMediaPlayerRenderer::GetMediaTime() {
  if (media_player_) {
    return media_player_->GetMediaTime();
  }
  return base::Milliseconds(0);
}

media::RendererType OHOSMediaPlayerRenderer::GetRendererType() {
  return media::RendererType::kOHOSMediaPlayer;
}

#if BUILDFLAG(ARKWEB_VIDEO_ASSISTANT)
void OHOSMediaPlayerRenderer::SetVideoSurface(int32_t surface_id) {
  LOG(INFO) << "OHOSMediaPlayerRenderer::SetVideoSurface, component surface_id:" << surface_id;
  if (media_player_) {
    media_player_->SetVideoSurface(surface_id);
  } else {
    LOG(INFO) << "SetVideoSurface, no media_player_";
  }
}
#endif // ARKWEB_VIDEO_ASSISTANT

void OHOSMediaPlayerRenderer::GetGrantMediaFileAccessDirs(std::vector<std::string>& grantMediaFileAccessDirs) {
  if (web_contents_.get() == nullptr) {
     LOG(ERROR) << "web contents is nullptr";
     return;
   }
  MediaSessionImpl* mediaSession = MediaSessionImpl::Get(web_contents_.get());
  if (mediaSession == nullptr) {
    LOG(ERROR) << "get mediaSession is nullptr";
    return;
  }
  // if setPathAllowingUniversalAccess called, use it
  if (!mediaSession->grantMediaFileAccessDirs_.empty()) {
    for (auto dir: mediaSession->grantMediaFileAccessDirs_) {
      grantMediaFileAccessDirs.emplace_back(dir);
    }
    return;
  }

  // if fileAccess is false. use default path
  if (!mediaSession->fileAccess_) {
    grantMediaFileAccessDirs.emplace_back("/data/storage/el1/bundle/entry/resources/resfile");
    LOG(INFO) << "USE DEFALUT PATH";
  }
}

#if BUILDFLAG(ARKWEB_PIP)
void OHOSMediaPlayerRenderer::PipEnable(bool enable) {
  LOG(INFO) << __func__ << " Pip enable:" << enable;
  if (media_player_) {
    media_player_->PipEnable(enable);
  } else {
    LOG(INFO) << "PipEnable, no media_player_";
  }
}
#endif

#if BUILDFLAG(ARKWEB_MEDIA_DMABUF)
void OHOSMediaPlayerRenderer::RecycleDmaBuffer() {
  if (media_player_) {
    media_player_->RecycleDmaBuffer();
  } else {
    LOG(INFO) << "DMABUF::RecycleDmaBuffer, no media_player_";
  }
}

void OHOSMediaPlayerRenderer::ResumeDmaBuffer() {
  if (media_player_) {
    media_player_->ResumeDmaBuffer();
  } else {
    LOG(INFO) << "DMABUF::ResumeDmaBuffer, no media_player_";
  }
}
#endif  // ARKWEB_MEDIA_DMABUF
}  // namespace content
